﻿using System;
using System.Reflection;
using System.Windows.Forms;
using System.ComponentModel;

using LeanEngine.Extensions;
using LeanEngine.Validation;
using System.Globalization;
using System.Text;

namespace LeanEngine.Validation.Windows.Forms
{
    /// <summary>
    /// Represents the behavior necessary to provide integration with the validation and a windows control item.
    /// </summary>
    public sealed class ValidatedProperty : IDisposable
    {
        #region Declaration Members

        internal const string DEFAULT_VALIDATED_PROPERTY = "Text";

        private Type _sourceType;

        #endregion

        #region Constructor Members

        /// <summary>
        /// Initialize a new instance of the <see cref="ValidatedProperty"/> class with a <see cref="ValidationProvider"/> and a <see cref="Control"/>.
        /// </summary>
        /// <param name="validationProvider">The validation provider.</param>
        /// <param name="control">The control to validate.</param>
        public ValidatedProperty(PropertyValidatorProvider validationProvider, Control control)
        {
            if (control == null) throw new ArgumentNullException("control");

            this.ValidationProvider = validationProvider;
            this.Control = control;
            this.Control.Validating += new CancelEventHandler(OnValidating);
            this.ValidatedPropertyName = ValidatedProperty.DEFAULT_VALIDATED_PROPERTY;
        }

        #endregion

        #region PropertyInfo Members

        /// <summary>
        /// Gets the control to validate.
        /// </summary>
        /// <value>
        /// The control to validate.
        /// </value>
        public Control Control { get; private set; }

        ///<summary>
        /// Gets if the control is valid.
        ///</summary>
        /// <value>
        /// true if the control is valid; otherwise, false.
        /// </value>
        public bool IsValid { get; set; }

        /// <summary>
        /// Gets if the validation is performed.
        /// </summary>
        /// <value>
        /// true if the validation is performed; otherwise, false.
        /// </value>
        public bool PerformValidation { get; set; }

        ///<summary>
        /// Gets the source property name.
        ///</summary>
        /// <value>
        /// The source property name.
        /// </value>
        public string SourcePropertyName { get; set; }

        ///<summary>
        /// Gets the validated property name.
        ///</summary>
        /// <value>
        /// The validated property name.
        /// </value>
        public string ValidatedPropertyName { get; set; }

        /// <summary>
        /// Gets the <see cref="ValidationProvider"/> for the control.
        /// </summary>
        /// <value>
        /// The <see cref="ValidationProvider"/> for the control.
        /// </value>
        public PropertyValidatorProvider ValidationProvider { get; private set; }

        /// <summary>
        /// Gets the current error message
        /// </summary>
        public string ErrorMessage { get; internal set; }

        /// <summary>
        /// Gets or sets the control which will be displayed the error icon.
        /// </summary>
        public Control DisplayErrorIconControl { get; set; }

        /// <summary>
        /// Gets the source type
        /// </summary>
        /// <returns></returns>
        private Type ValidatedType
        {
            get
            {
                if (_sourceType == null)
                {
                    if (string.IsNullOrEmpty(ValidationProvider.SourceTypeName)) throw new InvalidOperationException("No source type name found.");//Resources.ExceptionNoSourceTypeName);
                    _sourceType = Type.GetType(ValidationProvider.SourceTypeName, false);
                    
                    if (_sourceType == null) throw new InvalidOperationException("Invalid source type '" + ValidationProvider.SourceTypeName);//Resources.ExceptionInvalidSourceTypeName);
                }

                return _sourceType;
            }
        }

        #endregion

        #region Method Members

        internal bool Validate()
        {
            object value = null;
            if (ValidatedProperty.DEFAULT_VALIDATED_PROPERTY.Equals(this.ValidatedPropertyName))
            {
                value = this.Control.Text;
            }
            else
            {
                try
                {
                    value = this.Control.GetType().InvokeMember(this.ValidatedPropertyName,
                                                          BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance,
                                                          null,
                                                          this.Control,
                                                          null,
                                                          CultureInfo.CurrentCulture);
                }
                catch (System.Exception e)
                {
                    throw new InvalidOperationException("PropertyInfo '" + this.ValidatedPropertyName + "' not found on type '" + this.Control.GetType().Name, e);
                }
            }


            Type propertyType = null;
            try
            {
                propertyType = ValidatedType.GetProperty(this.SourcePropertyName).PropertyType;
            }
            catch (NullReferenceException)
            {
                throw new InvalidOperationException("PropertyInfo '" + this.SourcePropertyName + "' not found on control '" + this.Control.Name + "'.");
            }

            try
            {
                value = Convert.ChangeType(value, propertyType);
            }
            catch (FormatException)
            {
                //value = propertyType.Assembly.CreateInstance(propertyType.FullName);
            }
            catch (InvalidCastException)
            {
                //value = propertyType.Assembly.CreateInstance(propertyType.FullName);
            }

            var property = ValidatedType.GetProperty(this.SourcePropertyName);
            var validationList = ValidatorMetadataCache.GetValidatorList(property);
            if (validationList.Count == 0)
            {
                return true;
            }
            else
            {
                var errorMessagesList = new StringBuilder();

                try
                {                    
                    ValidationHelper.Validate(value, validationList, errorMessagesList);
                    this.ErrorMessage = errorMessagesList.ToString();
                    this.IsValid = this.ErrorMessage.IsNullOrEmpty();

                    return this.IsValid;
                }
                finally
                {
                    errorMessagesList.Remove(0, errorMessagesList.Length);
                    errorMessagesList = null;
                }
            }
        }

        #endregion

        #region Event Members

        private void OnValidating(object source, CancelEventArgs e)
        {
            if (this.PerformValidation)
            {
                this.ValidationProvider.PerformValidation(this);
                e.Cancel = !this.IsValid;
            }
        }

        #endregion

        #region IDisposable Members

        ///<summary>
        /// Allows the item to dispose of any unmanaged resources.
        ///</summary>
        public void Dispose()
        {
            if (this.Control != null)
            {
                this.Control.Validating -= OnValidating;
                this.Control = null;
            }
        }

        #endregion
    }
}
